﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using MapEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace Dark_Dungeon
{
    [Serializable()]
    public sealed class EntityManager : ISerializable
    {
        private List<Player> players = new List<Player>();
        private List<Character> characters = new List<Character>();

        private Turno turno = Turno.Players; 

        #region -- Constructor --
        public EntityManager()
        {
        }
        #endregion


        #region -- Deserialización --
        public EntityManager(SerializationInfo info, StreamingContext ctxt)
        {
            players = (List<Player>)info.GetValue("players", typeof(List<Player>));
            characters = (List<Character>)info.GetValue("characters", typeof(List<Character>));
        }
        #endregion



        #region -- Serialización --
        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("players", players);
            info.AddValue("characters", characters);
        }
        #endregion

        static public void saveState(string fileName, EntityManager entityManager)
        {
            SaveFile save = new SaveFile(entityManager);
            Stream stream = null;

            try
            {
                stream = File.Open(@"save\" + fileName + ".ddef", FileMode.Create);
                BinaryFormatter bformatter = new BinaryFormatter();
                bformatter.Serialize(stream, save);
                stream.Close();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            finally
            {
                stream.Close();
            }

        }

        static public EntityManager loadState(string fileName)
        {
            SaveFile save = null;
            Stream stream = null;

            try
            {
                //Cargar entidades
                stream = File.Open(@"save\" + fileName + ".ddef", FileMode.Open);
                BinaryFormatter bformatter = new BinaryFormatter();
                save = (SaveFile)bformatter.Deserialize(stream);
                stream.Close();

                if (save.magic != 0x66656464)
                    throw new Exception("Formato de fichero .ddef incorrecto.");

                if (save.version.CompareTo(Config.Version) != 0)
                    throw new Exception("Versión de fichero .ddef incorrecta.");
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            finally
            {
                stream.Close();
            }

            return save.manager;
        }

        #region -- Gestión de entidades --

        // Gestión de Jugadores
        public void AddPlayer(Player player)
        {
            players.Add(player);
        }

        public void RemovePlayer(Player player)
        {
            players.Remove(player);
        }

        public Player GetPlayer()
        {
            return GetPlayer(0);
        }
        public Player GetPlayer(UInt16 n_player)
        {
            if (n_player > players.Count)
                return players.FirstOrDefault();
            else
                return players[n_player];
        }

        // Gestión de PNJ's
        public void AddCharacter(Character character)
        {
            characters.Add(character);
        }

        public void RemoveCharacter(Character character)
        {
            characters.Remove(character);
        }

        public Character FindCharacterByCoordinate(Vector2 Coordinate)
        {
            return characters.Find(p => p.getPosition() == Coordinate);
        }
        #endregion

        #region -- Update --

        public void Update(GameTime gameTime, TileMap map)
        {
            switch (turno)
            {
                #region -- Turno Jugador --
                case Turno.Players:
                    Player player = players.FirstOrDefault();
                    if (player != null)
                    {
                        if (player.IsAlive())
                        {
                            player.Update(gameTime, map, ref turno);

                            if (player.Attacking() != EntityDirection.NoDirection)
                            {
                                Character chara = FindCharacterByCoordinate(Extensions.CalculateNewPosition(player.Attacking(), player.getPosition()));
                                if (chara != null)
                                    player.AttackEnemy(ref chara);
                                else
                                    Debug.AddStaticText("No hay nada que atacar.");

                                turno = Turno.IA;
                            }
                        }

                    }
                    break;

                #endregion

                #region -- Turno IA --
                case Turno.IA:
                    List<Character> Deads = null;

                    // Actualizar los characters
                    foreach (Character character in characters)
                    {
                        Player playerchara = players.FirstOrDefault();
                        character.Update(gameTime, map, ref playerchara);
                        if (!character.IsAlive())
                        {
                            if (Deads == null)
                                Deads = new List<Character>();

                            Deads.Add(character);
                        }

                    }

                    // Eliminar characters muertos
                    if (Deads != null)
                    {
                        foreach (Character deadcharacter in Deads)
                            characters.Remove(deadcharacter);
                    }

                    turno = Turno.Players;
                    break;

                #endregion
            }
        }

        #endregion

        #region -- Dibujado --
        public void Draw(GameTime gameTime, SpriteBatch spriteBatch/*, Range2D range*/)
        {
            foreach (var player in players)
                player.Draw(gameTime, spriteBatch);

            foreach (var character in characters)
                character.Draw(gameTime, spriteBatch);

        }
        #endregion
    }

    public enum Turno
    {
        Players, IA
    }

    [Serializable()]
    class SaveFile : ISerializable
    {
        public UInt32 magic;
        public string version;

        public EntityManager manager;

        public SaveFile(EntityManager entityManager)
        {
            this.manager = entityManager;
            this.magic = 0x66656464;
            this.version = Config.Version;
        }

        #region -- Deserialización --
        public SaveFile(SerializationInfo info, StreamingContext ctxt)
        {
            magic = (UInt32)info.GetValue("magic", typeof(UInt32));
            version = (string)info.GetValue("version", typeof(string));
            manager = (EntityManager)info.GetValue("manager", typeof(EntityManager));
        }
        #endregion

        #region -- Serialización --
        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("magic", magic);
            info.AddValue("version", version);
            info.AddValue("manager", manager);
        }
        #endregion 
    }
}
